﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace DeleteRepeatedFiles
{
    public partial class Form1 : Form
    {
        class Chicken
        {
            public readonly System.IO.FileInfo fileInfo;
            public bool grouped = false;
            /// <summary>
            /// 重复出现了多少次。
            /// </summary>
            public int repeated = 0;

            public Chicken(string filename)
            {
                this.fileInfo = new System.IO.FileInfo(filename);
            }

            public override string ToString()
            {
                return string.Format("[{0}]{1}", this.repeated, this.fileInfo.FullName);
            }
        }

        class Group : List<Chicken> { }

        public Form1()
        {
            InitializeComponent();
        }

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            if (this.folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.txtFolder.Text = this.folderBrowserDialog1.SelectedPath;
                this.btnRefresh_Click(sender, e);
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            string folder = this.txtFolder.Text;
            if (!System.IO.Directory.Exists(folder))
            {
                MessageBox.Show("Invalid folder!");
                return;
            }

            this.btnRefresh.Enabled = false;
            List<Group> groupList = GroupFiles(folder);
            this.lstFiles.Items.Clear();
            MapTo(this.lstFiles, groupList);
            this.btnRefresh.Enabled = true;
        }

        private readonly Random seed = new Random();
        private static readonly Color[] backColors = new Color[] { 
            Color.AliceBlue, Color.AntiqueWhite, Color.Aqua, Color.Aquamarine, Color.Azure, 
            Color.Beige, Color.Bisque, Color.Black, Color.BlanchedAlmond, 
            Color.Blue, Color.BlueViolet, Color.Brown, Color.BurlyWood, 
            Color.CadetBlue, Color.Chartreuse, Color.Chocolate, Color.Coral, 
            Color.CornflowerBlue, Color.Cornsilk, Color.Crimson, Color.Cyan, 
            Color.DarkBlue, Color.DarkCyan, Color.DarkGoldenrod, Color.DarkGray, 
            Color.DarkGreen, Color.DarkKhaki, Color.DarkMagenta, Color.DarkOliveGreen, 
            Color.DarkOrange, Color.DarkOrchid, Color.DarkRed, Color.DarkSalmon, 
            Color.DarkSeaGreen, Color.DarkSlateBlue, Color.DarkSlateGray, 
            Color.DarkTurquoise, Color.DarkViolet, Color.DeepPink, Color.DeepSkyBlue, 
            Color.DimGray, Color.DodgerBlue, Color.Firebrick, Color.FloralWhite, 
            Color.ForestGreen, Color.Fuchsia, Color.Gainsboro, Color.GhostWhite, 
            Color.Gold, Color.Goldenrod, Color.Gray, Color.Green, Color.GreenYellow, 
            Color.Honeydew, Color.HotPink, Color.IndianRed, Color.Indigo, Color.Ivory, 
            Color.Khaki, Color.Lavender, Color.LavenderBlush, Color.LawnGreen, 
            Color.LemonChiffon, Color.LightBlue, Color.LightCoral, Color.LightCyan, 
            Color.LightGoldenrodYellow, Color.LightGray, Color.LightGreen, 
            Color.LightPink, Color.LightSalmon, Color.LightSeaGreen, Color.LightSkyBlue, 
            Color.LightSlateGray, Color.LightSteelBlue, Color.LightYellow, Color.Lime, 
            Color.LimeGreen, Color.Linen, Color.Magenta, Color.Maroon, 
            Color.MediumAquamarine, Color.MediumBlue, Color.MediumOrchid, 
            Color.MediumPurple, Color.MediumSeaGreen, Color.MediumSlateBlue, 
            Color.MediumSpringGreen, Color.MediumTurquoise, Color.MediumVioletRed, 
            Color.MidnightBlue, Color.MintCream, Color.MistyRose, Color.Moccasin, 
            Color.NavajoWhite, Color.Navy, Color.OldLace, Color.Olive, Color.OliveDrab, 
            Color.Orange, Color.OrangeRed, Color.Orchid, Color.PaleGoldenrod, 
            Color.PaleGreen, Color.PaleTurquoise, Color.PaleVioletRed, Color.PapayaWhip, 
            Color.PeachPuff, Color.Peru, Color.Pink, Color.Plum, Color.PowderBlue, 
            Color.Purple, Color.Red, Color.RosyBrown, Color.RoyalBlue, 
            Color.SaddleBrown, Color.Salmon, Color.SandyBrown, Color.SeaGreen, 
            Color.SeaShell, Color.Sienna, Color.Silver, Color.SkyBlue, Color.SlateBlue, 
            Color.SlateGray, Color.Snow, Color.SpringGreen, Color.SteelBlue, 
            Color.Tan, Color.Teal, Color.Thistle, Color.Tomato, Color.Transparent, 
            Color.Turquoise, Color.Violet, Color.Wheat, Color.White, Color.WhiteSmoke, 
            Color.Yellow, Color.YellowGreen, 
        };

        private void MapTo(ListBox listBox, List<Group> groupList)
        {
            for (int i = 0; i < groupList.Count; i++)
            {
                Group group = groupList[i];
                bool repeated = (group.Count > 1);
                if (repeated)
                {
                    for (int j = 0; j < group.Count; j++)
                    {
                        Chicken chicken = group[j];
                        listBox.Items.Add(chicken);
                    }
                    listBox.Items.Add(new Chicken("-------------"));
                }
            }
        }

        private List<Group> GroupFiles(string folder)
        {
            var query = (from filename in System.IO.Directory.GetFiles(folder, "*.*", System.IO.SearchOption.TopDirectoryOnly)
                         orderby (new System.IO.FileInfo(filename).Length)
                         select new Chicken(filename)).ToArray();
            var groups = new List<Group>();
            for (int i = 0; i < query.Length; i++)
            {
                Chicken left = query[i];
                if (left.grouped) { continue; }

                var info1 = left.fileInfo;
                var group = new Group();
                group.Add(left); left.grouped = true;
                groups.Add(group);
                for (int j = i + 1; j < query.Length; j++)
                {
                    Chicken right = query[j];
                    if (right.grouped) { continue; }

                    if (SameContent(left.fileInfo, right.fileInfo))
                    {
                        group.Add(right); right.grouped = true;
                    }
                }
            }
            for (int i = 0; i < groups.Count; i++)
            {
                Group group = groups[i];
                int repeated = group.Count;
                for (int j = 0; j < group.Count; j++)
                {
                    Chicken chicken = group[j];
                    chicken.repeated = repeated;
                }
            }

            return groups;
        }

        private static bool SameContent(System.IO.FileInfo info1, System.IO.FileInfo info2)
        {
            bool foundDiff = false;
            if (info1.Length != info2.Length) { foundDiff = true; }
            if (!foundDiff)
            {
                using (var r1 = System.IO.File.OpenRead(info1.FullName))
                using (var r2 = System.IO.File.OpenRead(info2.FullName))
                {
                    while (r1.Position < r1.Length)
                    {
                        int a = r1.ReadByte(); int b = r2.ReadByte();
                        if (a != b)
                        {
                            foundDiff = true;
                            break;
                        }
                    }
                }
            }

            return !foundDiff;
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            var removed = new List<Chicken>();
            foreach (Chicken item in this.lstFiles.SelectedItems)
            {
                try
                {
                    if (System.IO.File.Exists(item.fileInfo.FullName))
                    {
                        System.IO.File.Delete(item.fileInfo.FullName);
                        removed.Add(item);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }

            foreach (var item in removed)
            {
                lstFiles.Items.Remove(item);
            }
        }
    }
}
